package com.tbit.utils;

import cn.hutool.core.util.NumberUtil;
import com.tbit.uqbike.object.business.Point;
import com.tbit.uqbike.webmanager.util.StringUtils;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.Polygon;

import java.util.ArrayList;
import java.util.List;

/**
 * 电子围栏工具类
 *
 * @author Leon
 * 2017年11月30日 上午9:11:14
 */
public class GeoUtil {

    /**
     * 经纬度字符串转points
     *
     * @param pointString
     * @return
     */
    public static List<Coordinate> getCoordinate(String pointString) {
        List<Coordinate> coordinates = new ArrayList<Coordinate>();

        String[] pointArray = pointString.split(";");
        for (int i = 0; i < pointArray.length; i++) {
            String[] lonlat = pointArray[i].split(",");
            if (lonlat.length == 2) {
                coordinates.add(new Coordinate(Double.parseDouble(lonlat[0]), Double.parseDouble(lonlat[1])));
            }
        }
        String[] lonlat = pointArray[0].split(",");
        coordinates.add(new Coordinate(Double.parseDouble(lonlat[0]), Double.parseDouble(lonlat[1])));
        return coordinates;
    }

    /**
     * 经纬度字符串转points
     *
     * @param pointString
     * @return
     */
    public static List<Point> getPoints(String pointString) {
        List<Point> points = new ArrayList<Point>();
        if (StringUtils.isNotNull(pointString)) {
            String[] pointArray = pointString.split(";");
            for (int i = 0; i < pointArray.length; i++) {
                String[] lonlat = pointArray[i].split(",");
                if (lonlat.length == 2) {
                    points.add(new Point(Double.parseDouble(lonlat[0]), Double.parseDouble(lonlat[1])));
                }
            }
        }
        return points;
    }

    /**
     * Points转字符串
     *
     * @param points
     * @return
     */
    public static String getPointToString(List<Point> points) {
        StringBuilder result = new StringBuilder();
        int size = points.size();
        for (int i = 0; i < size; i++) {
            Point point = points.get(i);
            result.append(point.getX()).append(",").append(point.getY());

            if (i != (size - 1)) {
                result.append(";");
            }
        }

        return result.toString();
    }

    /**
     * 获取电子围栏的中心
     *
     * @param points
     * @return
     */
    public static Point GetCenterPoint(List<Point> points) {
        // 以下为简化方法（400km以内）
        int total = points.size();
        double lat = 0, lon = 0;
        for (Point p : points) {
            lat += p.getX() * Math.PI / 180;
            lon += p.getY() * Math.PI / 180;
        }
        lat /= total;
        lon /= total;
        return new Point(lat * 180 / Math.PI, lon * 180 / Math.PI);
    }

    /**
     * 获取电子围栏的重心点
     *
     * @param mPoints
     * @return
     */
    public static Point getCenterOfGravityPoint(List<Point> mPoints) {
        double area = 0.0;//多边形面积
        double Gx = 0.0, Gy = 0.0;// 重心的x、y
        for (int i = 1; i <= mPoints.size(); i++) {
            double iLat = mPoints.get(i % mPoints.size()).getX();
            double iLng = mPoints.get(i % mPoints.size()).getY();
            double nextLat = mPoints.get(i - 1).getX();
            double nextLng = mPoints.get(i - 1).getY();
            double temp = (iLat * nextLng - iLng * nextLat) / 2.0;
            area += temp;
            Gx += temp * (iLat + nextLat) / 3.0;
            Gy += temp * (iLng + nextLng) / 3.0;
        }
        Gx = Gx / area;
        Gy = Gy / area;
        Double X = Double.valueOf(String.valueOf(Gx).substring(0, String.valueOf(Gx).indexOf(".") + 7));
        Double Y = Double.valueOf(String.valueOf(Gy).substring(0, String.valueOf(Gy).indexOf(".") + 7));
        return new Point(X, Y);
    }

    /**
     * 获取电子围栏的内心点
     *
     * @param CoordinateList
     * @return
     */
    public static Point getCenterOfGravityPoint4(List<Coordinate> CoordinateList) {
        Coordinate[] coordinates = new Coordinate[CoordinateList.size()];
        CoordinateList.toArray(coordinates);
        GeometryFactory geometryFactory = new GeometryFactory();
        Polygon p = geometryFactory.createPolygon(coordinates);//创建多边形
        org.locationtech.jts.geom.Point pt = p.getInteriorPoint();//多边形内心
        return new Point(pt.getX(), pt.getY());
    }

    /***
     * 获取点到多边形的距离
     * @param point
     * @param points
     * @return
     */
    public static Double getDistance(Point point, List<Point> points) {
        /** 最近的边 */
        Double distanceShort = null;
        Integer len = points.size();
        Integer maxIndex = len - 1;
        for (int i = 0; i < len; i++) {
            // 多边形中当前点
            Point currentPoint = points.get(i);
            Point nearPoint = maxIndex == i ? points.get(0) : points.get(i + 1);
            double a, b, c;
            a = getDistance(point, currentPoint);// 点与多边形一点的距离
            b = getDistance(point, nearPoint);// 点与多边形一点的距离
            c = getDistance(currentPoint, nearPoint);// 多边形邻边距离
            if (a * a >= b * b + c * c) {
                /** a为斜边 */
                if (distanceShort == null || distanceShort > b) {
                    distanceShort = b;
                }

                continue;
            }
            if (b * b >= c * c + a * a) {
                /** b为斜边 */
                if (distanceShort == null || distanceShort > a) {
                    distanceShort = a;
                }

                continue;
            }

            double l = (a + b + c) / 2; // 周长的一半
            double s = Math.sqrt(l * (l - a) * (l - b) * (l - c)); // 海伦公式求面积
            Double ss = 2 * s / c;
            if (distanceShort == null) {
                /** c为斜边，最近距离 */
                distanceShort = ss;
            }
        }
        return distanceShort;
    }

    /**
     * 检测点是否在多边形内
     *
     * @param point
     * @param points
     * @return
     */
    public static boolean IsPtInPoly(Point point, List<Point> points) {
        boolean flag = false;
        int n = points.size();
        Point dp = point;
        Point[] dps = new Point[n];
        points.toArray(dps);
        for (int i = 0; i < n; i++) {
            if (dp.getY() < dps[i].getY() && dp.getY() < dps[(i + 1) % n].getY()) {
                continue;
            }
            if (dps[i].getX() <= dp.getX() && dps[(i + 1) % n].getX() <= dp.getX()) {
                continue;
            }
            double dx = dps[(i + 1) % n].getX() - dps[i].getX();
            double dy = dps[(i + 1) % n].getY() - dps[i].getY();
            double t = (dp.getX() - dps[i].getX()) / dx;
            double y = t * dy + dps[i].getY();
            if (y <= dp.getY() && t >= 0 && t <= 1) {
                flag = !flag;
            }
        }
        return flag;
    }

    /**
     * 获取两点之间的距离
     */
    public static double getDistance(Point p1, Point p2) {
        double lon1 = (Math.PI / 180) * p1.getX();
        double lon2 = (Math.PI / 180) * p2.getX();
        double lat1 = (Math.PI / 180) * p1.getY();
        double lat2 = (Math.PI / 180) * p2.getY();

        // 地球半径
        double R = 6371;
        double d = Math.acos(Math.sin(lat1) * Math.sin(lat2) + Math.cos(lat1) * Math.cos(lat2) * Math.cos(lon2 - lon1)) * R;
        // 转换成千米（根据自己需求）

        return d * 1000;
    }

    /**
     * 经纬度校验
     * 只校验正数 0-90.000000 0-180.000000 范围内
     */
    public static boolean checkLoLa(Point point) {
        String regLo = "(((?:[0-9]|[1-9][0-9]|1[0-7][0-9])\\.([0-9]{0,6}))|((?:180)\\.([0]{0,6}))|((?:[0-9]|[1-9][0-9]|1[0-7][0-9]))|(?:180))";
        String regLa = "(((?:[0-9]|[1-8][0-9]|90)\\.([0-9]{0,6}))|(?:[0-9]|[1-8][0-9]|90))";
        String longitude = NumberUtil.roundDown(point.getX(), 6).toString();
        String latitude = NumberUtil.roundDown(point.getY(), 6).toString();
        return longitude.matches(regLo) == true ? latitude.matches(regLa) : false;
    }

    /**
     * 获取多边形面积
     */
    public static Integer pointArea(List<Point> points) {
        double S = points.get(0).getY() * (points.get(points.size() - 1).getX() - points.get(1).getX());
        for (int m = 1; m < points.size(); m++) {
            S += points.get(m).getY() * (points.get(m - 1).getX() - points.get((m + 1) % (points.size())).getX());
        }
        return (int) (Math.round(((Math.abs(S / 2) * 9101160000.085981))));
    }
}
